/**
	Pulsar engine. Test cases.
	Additional include file.

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

namespace tc 
{

// Internal Unit-test definition
struct TestDescription;

/// Type of failure
enum Failure
{
	/// TESTS_VERIFY or TESTS_ERROR message
	FAIL_EXPECTED_ERROR,
	/// Unexpected assertion
	FAIL_UNEXPECTED_ASSERTION,
	/// Expected assertion did not occured
	FAIL_LOST_EXPECTED_ASSERTION,
	/// Memory error
	FAIL_MEMORY_ERROR,
	/// Memory leak after test
	FAIL_MEMORY_LEAK,
	/// Thread leak after test
	FAIL_THREAD_LEAK,
	/// System exception
	FAIL_SYSTEM_EXCEPTION,
	/// Execution Time out
	FAIL_TIMEOUT,
	/// Return false from test case
	FAIL_TEST_RETURN
};


/// Type of callback
enum EntryType 
{
	/// Regular test
	ENTRY_TYPE_USUAL,
	/// First launch
	ENTRY_TYPE_FIRST,
	/// Last launch
	ENTRY_TYPE_LAST
};

/**
	Usual test callback 
*/
typedef bool (*CallbackTest)(EntryType entry, int index);

/*
	Test options and counters (internal structure)
*/
struct Options
{
	Options() :
	showUsage(false),
	bValidateAssert(true),
	bValidateMemory(true),
	bValidateThreads(true)
	{
		return;
	}

	pl::StdVector<pl::Filepath> testNameMask;
	bool showUsage;
	pl::Filepath logFileName;
	pl::Filepath logXMLFileName;
	bool bValidateAssert;
	bool bValidateMemory;
	bool bValidateThreads;
};



/**
	Base test suite class
*/
class TestSuite
{
public:


	/// Use TC_ADD to add test
	static void _addTest(CallbackTest cbFunc, int count, const pl_char* cbFuncName, int timeout = 1);
	/// Use TESTS_CHECK_MEM_LEAKS() to check memory leaks inside test
	static void checkMemLeaks(const pl_char* file, pl_uint32 line, const pl_char* func);
	/// Use TESTS_IGNORE_MEM_LEAKS() to disable memory checks after test
	static void ignoreMemLeaks();
	/// Use TESTS_CHECK_THREAD_LEAKS() to check threads inside test
	static void checkThreadLeaks(const pl_char* file, pl_uint32 line, const pl_char* func);
	/// Use TESTS_IGNORE_THREAD_LEAKS() to disable thread checks after test
	static void ignoreThreadLeaks();

	/// Use TESTS_VERIFY & TESTS_ERROR
	static void errorMessage(const pl_char* msg, const pl_char* file, pl_uint32 line, const pl_char* func);

	/// Use TESTS_EXPECT_ASSERT to give expected PL_ASSERTW/PL_ASSERTE
	static void expectedAssert(int counter, const pl_char* file, pl_uint32 line,  const pl_char* func);

	/// Use TESTS_VERIFY_PICTURE to test screenshot
	static void verifyPicture(const pl::Filepath& name);

	/// Set test name
	static void setTestSuiteName(const pl_char* name);

	/// Get test name
	static const pl_char* getTestSuiteName();

	inline void enableMemoryError(bool bEnable)
	{
		if (_pClass != NULL)
		{
			_pClass->_bMemoryErrorEnabled = bEnable;
		}
		return;
	}

	static inline void enableAllocError(bool bEnable)
	{
		if (_pClass != NULL)
		{
			_pClass->_bAllocErrorEnabled = bEnable;
		}
		return;
	}

	static inline bool isMemErrorEnabled()
	{
		if (_pClass == NULL)
		{
			return false;
		}
		return _pClass->_bMemoryErrorEnabled;
	}

	static inline tc::TestDescription* getCurrentTestPtr()
	{
		if (_pClass == NULL)
		{
			return NULL;
		}
		return _pClass->_currentTestPtr;
	}

	static void addTestFailure(const pl_char* msg, const pl_char* file, pl_uint32 line, const pl_char* func, Failure type);

	/// Initialize test suite (internal function)
	static bool _init();
	/// Shutdown test suite (internal function)
	static int _term();
	/// Execute next test stage (internal function)
	static bool _executeTests();


private:
	TestSuite();
	~TestSuite();

	/// Global Unit-test application class
	static TestSuite* _pClass;

	/// Parse command line options
	bool _parseCommandLine();

	/// Run next test
	void _onRunTest();

	/// Show test results
	int _printTestResult();

	virtual void onAfterShutdown(bool fFatalExit);

	void timeoutFunc();

	/// Current test number
	pl_uint32 _currentTestIndex;

	/// Pointer to current test
	tc::TestDescription* _currentTestPtr;

	/// Test case name
	pl::String<256> _testSuitName;

	bool _bMemoryErrorEnabled;
	bool _bAllocErrorEnabled;

	/// Thread to check test timeouts
	pl::Thread* _timeoutThread;

	/// Total timeout
	int _totalTimeout;

	bool _exitFlag;

	Options _options;

	pl::MemoryMonitorStat _memStat;
};

} // namespace tc

//
// End of file 'testcase_app.h'
//
